// 最小可用 MQTT 测试CLI：支持动态注册与登录
// 依赖：现有 mosquitto 封装 ems::protocol::MqttClient、配置加载 ems::utils::Config

#include "protocol/mqtt/MqttClient.h"
#include "utils/Config.h"
#include "core/Logger.h"

#include <openssl/hmac.h>
#include <openssl/evp.h>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <chrono>
#include <thread>
#include <vector>
#include <algorithm>
#include <sys/stat.h>

using ems::protocol::MqttClient;
using ems::protocol::MqttQoS;
using ems::utils::Config;
using ems::utils::MqttConfig;

static std::string hexEncode(const unsigned char* data, size_t len) {
    static const char* hex = "0123456789abcdef";
    std::string out;
    out.resize(len * 2);
    for (size_t i = 0; i < len; ++i) {
        out[2*i]   = hex[(data[i] >> 4) & 0xF];
        out[2*i+1] = hex[data[i] & 0xF];
    }
    return out;
}

static std::string hmacSha256Hex(const std::string& key, const std::string& content) {
    unsigned char md[EVP_MAX_MD_SIZE];
    unsigned int md_len = 0;
    HMAC(EVP_sha256(), key.data(), static_cast<int>(key.size()),
         reinterpret_cast<const unsigned char*>(content.data()), content.size(), md, &md_len);
    return hexEncode(md, md_len);
}

static std::string randomDigits(int n) {
    std::string s; s.reserve(n);
    for (int i = 0; i < n; ++i) s.push_back(static_cast<char>('0' + (rand() % 10)));
    return s;
}

static std::string buildContentString(const std::string& authType, const std::string& deviceSn, const std::string& deviceType, const std::string& random, int securemode) {
    // 将 deviceSn、deviceType、authType、securemode、random 按字母顺序排序、拼接（无分隔符）
    // authType, deviceSn, deviceType, random, securemode
    std::ostringstream oss;
    oss << "authType" << authType
        << "deviceSn" << deviceSn
        << "deviceType" << deviceType
        << "random" << random
        << "securemode" << securemode;
    return oss.str();
}

static std::string buildClientId(const std::string& deviceType, const std::string& deviceSn, const std::string& authType, const std::string& random, int securemode) {
    // {deviceType}.{deviceSn}|securemode=1/2,authType=register/login,random=xxxx,signMethod=hmacsha256
    std::ostringstream oss;
    oss << deviceType << "." << deviceSn
        << "|securemode=" << securemode
        << ",authType=" << authType
        << ",random=" << random
        << ",signMethod=hmacsha256";
    return oss.str();
}

static std::string loadDeviceSecretPath(const std::string& cfgRoot, const std::string& deviceSn) {
    std::ostringstream oss;
    oss << cfgRoot << "/device_secret_" << deviceSn << ".txt";
    return oss.str();
}

static bool writeTextFile(const std::string& path, const std::string& content) {
    std::ofstream ofs(path, std::ios::out | std::ios::trunc);
    if (!ofs.is_open()) return false;
    ofs << content;
    return ofs.good();
}

static bool readTextFile(const std::string& path, std::string& out) {
    std::ifstream ifs(path);
    if (!ifs.is_open()) return false;
    std::ostringstream ss; ss << ifs.rdbuf();
    out = ss.str();
    out.erase(std::remove(out.begin(), out.end(), '\r'), out.end());
    out.erase(std::remove(out.begin(), out.end(), '\n'), out.end());
    return true;
}

static void usage() {
    std::cout << "用法: mqtt-cli --config <config_dir> <register|login>" << std::endl;
    std::cout << " 示例: mqtt-cli --config ./config register" << std::endl;
    std::cout << "       mqtt-cli --config ./config login" << std::endl;
}

int main(int argc, char** argv) {
    std::string configDir = "./config";
    std::string mode;

    for (int i = 1; i < argc; ++i) {
        std::string a = argv[i];
        if (a == "--config" && i + 1 < argc) { configDir = argv[++i]; }
        else if (a == "register" || a == "login") { mode = a; }
        else if (a == "-h" || a == "--help") { usage(); return 0; }
    }

    if (mode.empty()) { usage(); return 1; }

    // 载入配置
    auto& cfg = Config::getInstance();
    if (!cfg.load(configDir)) {
        std::cerr << "ERROR: 加载配置失败: " << configDir << std::endl;
        return 2;
    }
    const MqttConfig& m = cfg.getMqtt();

    // 从配置读取核心字段
    const std::string host = m.host;
    const int port = m.port;
    const bool useTls = m.useTls; // 若需要 TLS 请在 mqtt.conf 配置 ca/cert/key
    const int keepAlive = m.keepAlive > 0 ? m.keepAlive : 60;
    const bool cleanSession = m.cleanSession;
    const int qos = (m.qos >= 0 && m.qos <= 2) ? m.qos : 1;

    const std::string deviceSn = !m.deviceSn.empty() ? m.deviceSn : m.clientId;
    const std::string deviceType = m.deviceType;
    const std::string deviceTypeSecret = m.deviceTypeSecret;

    if (deviceSn.empty() || deviceType.empty()) {
        std::cerr << "ERROR: 配置缺少 client.device_sn 或 client.device_type" << std::endl;
        return 3;
    }

    // topic: 注册下行、登录上行等
    auto itPrefix = m.topics.find("topic_prefix");
    const std::string topicPrefixTpl = (itPrefix != m.topics.end()) ? itPrefix->second : std::string("/ext/${deviceSn}/register");
    auto renderPrefix = [&](const std::string& sn) {
        std::string t = topicPrefixTpl;
        size_t pos = t.find("${deviceSn}");
        if (pos != std::string::npos) t.replace(pos, std::string("${deviceSn}").size(), sn);
        return t;
    };

    const std::string topicRegister = renderPrefix(deviceSn);
    const std::string topicUpLogin = (m.topics.count("login") ? m.topics.at("login") : std::string("up/gateway/login"));

    // 计算 CONNECT 凭据
    const int securemode = useTls ? 1 : 2; // 文档：1 TLS，2 非TLS
    const std::string random = randomDigits(8);
    const std::string content = buildContentString(mode, deviceSn, deviceType, random, securemode);

    // 注册与登录的密码：
    // register -> HMAC(DeviceTypeSecret, content)
    // login    -> HMAC(DeviceSecret, content)
    std::string password;
    std::string username = deviceSn + "&" + deviceType; // 文档规则
    std::string clientId;
    std::string deviceSecret;

    if (mode == "register") {
        if (deviceTypeSecret.empty()) {
            std::cerr << "ERROR: security.device_type_secret 为空，无法注册" << std::endl;
            return 4;
        }
        password = hmacSha256Hex(deviceTypeSecret, content);
        clientId = buildClientId(deviceType, deviceSn, "register", random, securemode);
    } else {
        // login
        const std::string secretPath = loadDeviceSecretPath(configDir, deviceSn);
        if (!readTextFile(secretPath, deviceSecret) || deviceSecret.empty()) {
            std::cerr << "ERROR: 未找到设备密钥，请先注册。文件: " << secretPath << std::endl;
            return 5;
        }
        password = hmacSha256Hex(deviceSecret, content);
        clientId = buildClientId(deviceType, deviceSn, "login", random, securemode);
    }

    // 初始化 MQTT 客户端
    MqttClient cli;
    cli.setKeepAlive(keepAlive);
    cli.setCleanSession(cleanSession);
    cli.setAutoReconnect(false);

    if (!cli.initialize(clientId, host, port)) {
        std::cerr << "ERROR: 初始化客户端失败" << std::endl;
        return 6;
    }
    if (!cli.setCredentials(username, password)) {
        std::cerr << "ERROR: 设置用户名/密码失败: " << cli.getLastError() << std::endl;
        return 7;
    }
    if (useTls) {
        if (!m.caFile.empty() || !m.certFile.empty() || !m.keyFile.empty()) {
            if (!cli.setTLS(m.caFile, m.certFile, m.keyFile)) {
                std::cerr << "ERROR: 设置TLS失败: " << cli.getLastError() << std::endl;
                return 8;
            }
        } else {
            // 若没有证书路径也尝试直连（取决于 mosquitto 的默认CA 配置）
        }
    }

    std::atomic<bool> connected{false};
    std::atomic<bool> gotSecret{false};
    std::string receivedSecret;

    cli.setConnectCallback([&](bool ok){
        connected = ok;
        std::cout << (ok ? "CONNECTED" : "CONNECT FAILED") << std::endl;
    });

    cli.setMessageCallback([&](const std::string& topic, const std::string& payload){
        std::cout << "RECV topic=" << topic << "\n" << payload << std::endl;
        if (mode == "register" && topic == topicRegister) {
            // 简单解析 deviceSecret（不引入 JSON 依赖，直接查找字段）
            auto pos = payload.find("\"deviceSecret\"");
            if (pos != std::string::npos) {
                auto colon = payload.find(':', pos);
                if (colon != std::string::npos) {
                    auto q1 = payload.find('"', colon);
                    auto q2 = (q1 == std::string::npos) ? q1 : payload.find('"', q1 + 1);
                    if (q1 != std::string::npos && q2 != std::string::npos && q2 > q1) {
                        receivedSecret = payload.substr(q1 + 1, q2 - q1 - 1);
                        gotSecret = !receivedSecret.empty();
                    }
                }
            }
        }
    });

    // 连接
    if (!cli.connect()) {
        std::cerr << "ERROR: 连接失败: " << cli.getLastError() << std::endl;
        return 9;
    }

    // 循环线程
    cli.startLoop();

    // 等待连接建立
    for (int i = 0; i < 100 && !connected; ++i) std::this_thread::sleep_for(std::chrono::milliseconds(50));
    if (!connected) {
        std::cerr << "ERROR: 超时未连接，请检查 host/port/TLS 与服务端策略" << std::endl;
        cli.stopLoop();
        cli.disconnect();
        return 10;
    }

    if (mode == "register") {
        // 订阅注册下行主题
        if (!cli.subscribe(topicRegister, static_cast<MqttQoS>(qos))) {
            std::cerr << "ERROR: 订阅注册主题失败: " << cli.getLastError() << std::endl;
            cli.stopLoop(); cli.disconnect();
            return 11;
        }
        std::cout << "SUB " << topicRegister << " ok" << std::endl;

        // 等待设备密钥
        int waitMs = 15000; // 最多等待 15s
        for (int i = 0; i < waitMs/100 && !gotSecret; ++i) std::this_thread::sleep_for(std::chrono::milliseconds(100));
        if (!gotSecret) {
            std::cerr << "ERROR: 未收到注册下发，请核对认证链路与 EMQX 插件" << std::endl;
            cli.stopLoop(); cli.disconnect();
            return 12;
        }
        // 保存 deviceSecret
        const std::string path = loadDeviceSecretPath(configDir, deviceSn);
        if (writeTextFile(path, receivedSecret)) {
            std::cout << "动态注册成功，deviceSecret 已保存: " << path << std::endl;
        } else {
            std::cout << "动态注册成功，但保存 deviceSecret 失败: " << path << std::endl;
        }
    } else {
        // login: 保持连接一段时间，用户可在服务端观察在线
        std::cout << "登录连接已建立（保持 5s 以便验证在线状态）" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(5));
    }

    cli.stopLoop();
    cli.disconnect();
    std::cout << "DONE" << std::endl;
    return 0;
}
